﻿using System;
using System.Diagnostics;

using dtStatus = System.UInt32;
#if DT_POLYREF64
using dtPolyRef = System.UInt64;
using dtTileRef = System.UInt64;
#else
using dtPolyRef = System.UInt32;
using dtTileRef = System.UInt32;
#endif

#if DT_POLYREF64
public static partial class Detour{
    static const uint DT_SALT_BITS = 16;
    static const uint DT_TILE_BITS = 28;
    static const uint DT_POLY_BITS = 20;
}
#endif

public static partial class Detour 
{
    public static int computeTileHash(int x, int y, int mask)
    {
        const uint h1 = 0x8da6b343; // Large multiplicative constants;
        const uint h2 = 0xd8163841; // here arbitrarily chosen primes
        uint n = (uint)(h1 * x + h2 * y);
        return (int)(n & mask);
    }
    public class dtNavMesh 
    {
        private dtNavMeshParams m_params;
        private float[] m_orig = new float[3];
        private float m_tileWidth, m_tileHeight;
        private int m_maxTiles;
        private int m_tileLutSize;
        private int m_tileLutMask;

        //dtMeshTile
        private dtMeshTile[] m_posLookup;
        private dtMeshTile m_nextFree;
        private dtMeshTile[] m_tiles;

        #if DT_POLYREF64
        #else
        uint m_saltBits;			
        uint m_tileBits;			
        uint m_polyBits;
        #endif

        public dtNavMesh() 
        {
            #if DT_POLYREF64
	        m_saltBits = 0;
	        m_tileBits = 0;
	        m_polyBits = 0;
            #endif
            m_params = new dtNavMeshParams();
            m_orig[0] = 0;
            m_orig[1] = 0;
            m_orig[2] = 0;
        }
        public dtStatus init(dtNavMeshParams navMeshParams)
        {
            m_params = navMeshParams.Clone();
            dtVcopy(m_orig, navMeshParams.orig);
            m_tileWidth = navMeshParams.tileWidth;
            m_tileHeight = navMeshParams.tileHeight;

            //init tiles
            m_maxTiles = navMeshParams.maxTiles;
            m_tileLutSize = (int)dtNextPow2((uint)(navMeshParams.maxTiles / 4));
            if (m_tileLutSize == 0)
            {
                m_tileLutSize = 1;
            }
            m_tileLutMask = m_tileLutSize - 1;
            m_tiles = new dtMeshTile[m_maxTiles];
            dtcsArrayItemsCreate(m_tiles);
            if (m_tiles == null)
            {
                return (DT_FAILURE | DT_OUT_OF_MEMORY);
            }
            m_posLookup = new dtMeshTile[m_tileLutSize];
            dtcsArrayItemsCreate(m_posLookup);
            if (m_posLookup == null)
            {
                return DT_FAILURE | DT_OUT_OF_MEMORY;
            }
            m_nextFree = null;
            for (int i = m_maxTiles - 1; i <= 0; --i)
            {
                m_tiles[i].salt = 1;
                m_tiles[i].next = m_nextFree;
                m_nextFree = m_tiles[i];
            }

#if DT_POLYREF64
#else
            m_tileBits = (dtStatus)dtIlog2(dtNextPow2((uint)navMeshParams.maxTiles));
            m_polyBits = (dtStatus)dtIlog2(dtNextPow2((uint)navMeshParams.maxPolys));
            m_saltBits = Math.Min((uint)31, 32 - m_tileBits - m_polyBits);
            if (m_saltBits < 10)
            {
                return DT_FAILURE | DT_INVALID_PARAM;
            }
#endif

            return DT_SUCCESS;
        }

        public dtStatus init(dtRawTileData rawTile, int flags)
        {
            dtMeshHeader header = rawTile.header;
            if (header.magic != DT_NAVMESH_MAGIC)
            {
                return DT_FAILURE | DT_WRONG_MAGIC;
            }
            if (header.version != DT_NAVMESH_VERSION)
            {
                return DT_FAILURE | DT_WRONG_MAGIC;
            }
            dtNavMeshParams navMeshParams = new dtNavMeshParams();
            dtVcopy(navMeshParams.orig, header.bmin);
            navMeshParams.tileWidth = header.bmax[0] - header.bmin[0];
            navMeshParams.tileHeight = header.bmax[2] - header.bmin[2];
            navMeshParams.maxTiles = 1;
            navMeshParams.maxPolys = header.polyCount;

            dtStatus status = init(navMeshParams);
            if (dtStatusFailed(status))
            {
                return status;
            }
            dtTileRef dummyResult = 0;
            return addTile(rawTile, flags, 0, ref dummyResult);
        }
        public dtMeshTile getTileAt(int x,int y,int layer) 
        {
            int h = computeTileHash(x,y,m_tileLutMask);
            dtMeshTile tile = m_posLookup[h];
            while (tile != null) 
            {
                if (tile.header != null && tile.header.x == x && tile.header.y == y && tile.header.layer == layer) 
                {
                    return tile;
                }
                tile = tile.next;
            }
            return null;
        }
        public dtPolyRef encodePolyId(uint salt,uint it,uint ip) 
        {
           #if DT_POLYREF64
		   return ((dtPolyRef)salt << (DT_POLY_BITS+DT_TILE_BITS)) | ((dtPolyRef)it << DT_POLY_BITS) | (dtPolyRef)ip;
           #else
           return ((dtPolyRef)salt << (int)(m_polyBits + m_tileBits)) | ((dtPolyRef)it << (int)m_polyBits) | (dtPolyRef)ip;
           #endif
        }

        public uint decodePolyIdPoly(dtPolyRef polyRef)
        {
           #if DT_POLYREF64
		    const dtPolyRef polyMask = ((dtPolyRef)1<<DT_POLY_BITS)-1;
		    return (uint)(ref & polyMask);
           #else
            dtPolyRef polyMask = (dtPolyRef)((1 << (int)m_polyBits) - 1);
            return (uint)(polyRef & polyMask);
           #endif
        }
        public void decodePolyId(dtPolyRef polyRef,ref uint salt,ref uint it,ref uint ip) 
        {
           #if DT_POLYREF64
		   const dtPolyRef saltMask = ((dtPolyRef)1<<DT_SALT_BITS)-1;
		   const dtPolyRef tileMask = ((dtPolyRef)1<<DT_TILE_BITS)-1;
		   const dtPolyRef polyMask = ((dtPolyRef)1<<DT_POLY_BITS)-1;
		   salt = (uint)((ref >> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask);
		   it = (uint)((ref >> DT_POLY_BITS) & tileMask);
		   ip = (uint)(ref & polyMask);
           #else
            dtPolyRef saltMask = (dtPolyRef)(1 << (int)m_saltBits) - 1;
            dtPolyRef tileMask = (dtPolyRef)((1 << (int)m_tileBits) - 1);
            dtPolyRef polyMask = (dtPolyRef)((1 << (int)m_polyBits) - 1);
            salt = (uint)((polyRef >> (int)(m_polyBits + m_tileBits)) & saltMask);
            it = (uint)((polyRef >> (int)m_polyBits) & tileMask);
            ip = (uint)(polyRef & polyMask);
           #endif
        }
        public uint decodePolyIdTile(dtPolyRef polyRef) 
        {
            #if DT_POLYREF64
		    const dtPolyRef tileMask = ((dtPolyRef)1<<DT_TILE_BITS)-1;
		    return (uint)((ref >> DT_POLY_BITS) & tileMask);
            #else
            dtPolyRef tileMask = (dtPolyRef)((1 << (int)m_tileBits) - 1);
            return (uint)((polyRef >> (int)m_polyBits) & tileMask);
            #endif
        }

        public uint decodePolyIdSalt(dtPolyRef polyRef)
        {
           #if DT_POLYREF64
		    const dtPolyRef saltMask = ((dtPolyRef)1<<DT_SALT_BITS)-1;
		    return (uint)((ref >> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask);
           #else
            dtPolyRef saltMask = (dtPolyRef)((1 << (int)m_saltBits) - 1);
            return (uint)((polyRef >> (int)(m_polyBits + m_tileBits)) & saltMask);
           #endif
        }
        public int getTilesAt(int x, int y, dtMeshTile[] tiles, int maxTiles)
        {
            int n = 0;

            // Find tile based on hash.
            int h = computeTileHash(x, y, m_tileLutMask);
            dtMeshTile tile = m_posLookup[h];
            while (tile != null)
            {
                if (tile.header != null &&
                    tile.header.x == x &&
                    tile.header.y == y)
                {
                    if (n < maxTiles)
                        tiles[n++] = tile;
                }
                tile = tile.next;
            }

            return n;
        }
        public dtPolyRef getPolyRefBase(dtMeshTile tile) 
        {
            if (tile == null) return 0;
            uint it = (uint)Array.IndexOf(m_tiles,tile);
            return encodePolyId(tile.salt,it,0);
        }
        public int queryPolygonsInTile(dtMeshTile tile,float[] qmin,float[] qmax,dtPolyRef[] polys,int maxPolys) 
        {
            if (tile.bvTree != null)
            {
                int endNodeIndex = tile.header.bvNodeCount;
                float[] tbmin = tile.header.bmin;
                float[] tbmax = tile.header.bmax;
                float qfac = tile.header.bvQuantFactor;

                ushort[] bmin = new ushort[3];
                ushort[] bmax = new ushort[3];

                float minx = dtClamp(qmin[0], tbmin[0], tbmax[0]) - tbmin[0];
                float miny = dtClamp(qmin[1], tbmin[1], tbmax[1]) - tbmin[1];
                float minz = dtClamp(qmin[2], tbmin[2], tbmax[2]) - tbmin[2];
                float maxx = dtClamp(qmax[0], tbmin[0], tbmax[0]) - tbmin[0];
                float maxy = dtClamp(qmax[1], tbmin[1], tbmax[1]) - tbmin[1];
                float maxz = dtClamp(qmax[2], tbmin[2], tbmax[2]) - tbmin[2];
                // Quantize
                bmin[0] = (ushort)((ushort)(qfac * minx) & 0xfffe);
                bmin[1] = (ushort)((ushort)(qfac * miny) & 0xfffe);
                bmin[2] = (ushort)((ushort)(qfac * minz) & 0xfffe);
                bmax[0] = (ushort)((ushort)(qfac * maxx + 1) | 1);
                bmax[1] = (ushort)((ushort)(qfac * maxy + 1) | 1);
                bmax[2] = (ushort)((ushort)(qfac * maxz + 1) | 1);

                // Traverse tree
                dtPolyRef polyRefBase = getPolyRefBase(tile);
                int n = 0;
                int curNode = 0;
                dtBVNode node = null;
                while (curNode < endNodeIndex)
                {
                    node = tile.bvTree[curNode];

                    bool overlap = dtOverlapQuantBounds(bmin, bmax, node.bmin, node.bmax);
                    bool isLeafNode = node.i >= 0;
                    if (isLeafNode && overlap)
                    {
                        if (n < maxPolys)
                        {
                            polys[n++] = polyRefBase | (dtPolyRef)node.i;
                        }
                    }

                    if (overlap || isLeafNode)
                    {
                        ++curNode;
                    }
                    else
                    {
                        int escapeIndex = -node.i;
                        curNode += escapeIndex;
                    }
                }
                return n;
            }
            else 
            {
                float[] bmin = new float[3];
                float[] bmax =new float[3];
                int n = 0;
                dtPolyRef polyRefBase = getPolyRefBase(tile);
                for (int i = 0; i < tile.header.polyCount;++i)
                {
                    dtPoly p = tile.polys[i];
                    if (p.getType() == (byte)dtPolyTypes.DT_POLYTYPE_OFFMESH_CONNECTION) 
                    {
                        continue;
                    }
                    int vIndex = p.verts[0] * 3;
                    dtVcopy(bmin,0,tile.verts,vIndex);
                    dtVcopy(bmax,0,tile.verts,vIndex);
                    for (int j = 1; j < p.vertCount;++j)
                    {
                        vIndex = p.verts[j] * 3;
                        dtVmin(bmin,0,tile.verts,vIndex);
                        dtVmax(bmax,0,tile.verts,vIndex);
                    }
                    if (dtOverlapBounds(qmin, qmax, bmin, bmax)) 
                    {
                        if (n < maxPolys) 
                        {
                            polys[n++] = polyRefBase | (dtPolyRef)i;
                        }
                    }
                }
                return n;
            }

         
        }

        public void getTileAndPolyByRefUnsafe(dtPolyRef polyRef, ref dtMeshTile tile, ref dtPoly poly)
        {
            uint salt = 0, it = 0, ip = 0;
            decodePolyId(polyRef, ref salt, ref it, ref ip);
            tile = m_tiles[it];
            poly = m_tiles[it].polys[ip];
        }

        public void getTileAndPolyByRefUnsafe(dtPolyRef polyRef, ref dtMeshTile tile, ref dtPoly poly, ref uint ip)
        {
            uint salt = 0, it = 0;
            decodePolyId(polyRef, ref salt, ref it, ref ip);
            tile = m_tiles[it];
            poly = m_tiles[it].polys[ip];
        }

        public void closestPointOnPoly(dtPolyRef polyRef, float[] pos, int posStart, float[] closest, ref bool posOverPoly)
        {
            dtMeshTile tile = null;
            dtPoly poly = null;
            uint ip = 0;
            getTileAndPolyByRefUnsafe(polyRef, ref tile, ref poly, ref ip);
            if (poly.getType() == (byte)dtPolyTypes.DT_POLYTYPE_OFFMESH_CONNECTION)
            {
                int v0Start = poly.verts[0] * 3;
                int v1Start = poly.verts[1] * 3;
                float d0 = dtVdist(pos, posStart, tile.verts, v0Start);
                float d1 = dtVdist(pos, posStart, tile.verts, v1Start);
                float u = d0 / (d0 + d1);
                dtVlerp(closest, 0, tile.verts, v0Start, tile.verts, v1Start, u);

                posOverPoly = false;
                return;
            }

            dtPolyDetail pd = tile.detailMeshes[ip];

            float[] verts = new float[DT_VERTS_PER_POLYGON * 3];
            float[] edged = new float[DT_VERTS_PER_POLYGON];
            float[] edget = new float[DT_VERTS_PER_POLYGON];
            int nv = poly.vertCount;
            for (int i = 0; i < nv; ++i)
            {
                dtVcopy(verts, i * 3, tile.verts, poly.verts[i] * 3);
            }

            dtVcopy(closest, 0, pos, posStart);
            if (!dtDistancePtPolyEdgesSqr(pos, posStart, verts, nv, edged, edget))
            {
                float dmin = float.MaxValue;
                int imin = -1;
                for (int i = 0; i < nv; ++i)
                {
                    if (edged[i] < dmin)
                    {
                        dmin = edged[i];
                        imin = i;
                    }
                }

                int vaStart = imin * 3;
                int vbStart = ((imin + 1) % nv) * 3;
                dtVlerp(closest, 0, verts, vaStart, verts, vbStart, edget[imin]);

                posOverPoly = false;
            }
            else
            {
                posOverPoly = true;
            }
            for (int j = 0; j < pd.triCount; ++j)
            {
                int tIndex = (int)(pd.triBase + j) * 4;
                int[] vIndices = new int[3];
                float[][] vArrays = new float[3][];
                for (int k = 0; k < 3; ++k)
                {
                    if (tile.detailTris[tIndex + k] < poly.vertCount)
                    {
                        vIndices[k] = poly.verts[tile.detailTris[tIndex + k]] * 3;
                        vArrays[k] = tile.verts;
                    }
                    else
                    {
                        vIndices[k] = (int)(pd.vertBase + (tile.detailTris[tIndex + k] - poly.vertCount)) * 3;
                        vArrays[k] = tile.detailVerts;
                    }
                }
                float h = .0f; ;
                if (dtClosestHeightPointTriangle(pos, posStart, vArrays[0], vIndices[0], vArrays[1], vIndices[1], vArrays[2], vIndices[2], ref h))
                {
                    closest[1] = h;
                    break;
                }
            }
        }
        public static uint allocLink(dtMeshTile tile) 
        {
            if (tile.linksFreeList == Detour.DT_NULL_LINK) 
            {
                return DT_NULL_LINK;
            }
            uint link = tile.linksFreeList;
            tile.linksFreeList = tile.links[link].next;
            return link;
        }
        public void connectIntLinks(dtMeshTile tile) 
        {
            if (tile == null) 
            {
                return;
            }
            dtPolyRef polyRefBase = getPolyRefBase(tile);

            for (int i = 0; i < tile.header.polyCount;++i)
            {
                dtPoly poly = tile.polys[i];
                poly.firstLink = DT_NULL_LINK;
                if (poly.getType() == (byte)dtPolyTypes.DT_POLYTYPE_OFFMESH_CONNECTION) 
                {
                    continue;
                }
                for (int j = poly.vertCount - 1; j >= 0; --j) 
                {
                    if (poly.neis[j] == 0 || (poly.neis[j] & DT_EXT_LINK) != 0) 
                    {
                        continue;
                    }
                    uint idx = allocLink(tile);
                    if (idx != DT_NULL_LINK) 
                    {
                        dtLink link = tile.links[idx];
                        link.polyRef =polyRefBase | (dtPolyRef)(poly.neis[j]-1);
                        link.edge =(byte)j;
                        link.side = 0xff;
                        link.bmin = link.bmax = 0;
                        link.next = poly.firstLink;
                        poly.firstLink = idx;
                    }
                 
                }
            }
           
        }

        public void baseOffMeshLinks(dtMeshTile tile) 
        {
            if (tile == null) 
            {
                return;
            }
            dtPolyRef polyRefBase = getPolyRefBase(tile);
            for (int i = 0; i < tile.header.offMeshConCount;++i)
            {
              dtOffMeshConnection con  =tile.offMeshCons[i];
              dtPoly poly =tile.polys[con.poly];
              float[] ext = new float[] { con.rad, tile.header.walkableClimb, con.rad };
              float[] nearestPt =new float[3];
              dtPolyRef polyRef = findNearestPolyInTile(tile, con.pos, 0, ext, nearestPt);
              if (polyRef == 0)
              {
                  continue;
              }
              if (dtSqr(nearestPt[0] - con.pos[0]) + dtSqr(nearestPt[2] - con.pos[2]) > dtSqr(con.rad))
              {
                  continue;
              }
              int vIndex = poly.verts[0] * 3;
              dtVcopy(tile.verts, vIndex, nearestPt, 0);

              uint idx = allocLink(tile);
              if (idx != DT_NULL_LINK)
              {
                  dtLink link = tile.links[idx];
                  link.polyRef = polyRef;
                  link.edge = (byte)0;
                  link.side = 0xff;
                  link.bmin = link.bmax = 0;
                  // Add to linked list.
                  link.next = poly.firstLink;
                  poly.firstLink = idx;
              }

              // Start end-point is always connect back to off-mesh connection. 
              uint tidx = allocLink(tile);
              if (tidx != DT_NULL_LINK)
              {
                  ushort landPolyIdx = (ushort)decodePolyIdPoly(polyRef);
                  dtPoly landPoly = tile.polys[landPolyIdx];
                  dtLink link = tile.links[tidx];
                  link.polyRef = polyRefBase | (dtPolyRef)(con.poly);
                  link.edge = 0xff;
                  link.side = 0xff;
                  link.bmin = link.bmax = 0;
                  // Add to linked list.
                  link.next = landPoly.firstLink;
                  landPoly.firstLink = tidx;
              }
            }
        }

        public dtPolyRef findNearestPolyInTile(dtMeshTile tile,float[] center,int centerStart,float[] extents,float[] nearestPt) 
        {
           float[] bmin= new float[3];
           float[] bmax = new float[3];
           dtVsub(bmin,0,center,centerStart,extents,0);
           dtVadd(bmax,0,center,centerStart,extents,0);
           
           dtPolyRef[] polys =new dtPolyRef[128];
           int polyCount = queryPolygonsInTile(tile,bmin,bmax,polys,128);
           dtPolyRef nearest = 0;

           float nearestDistanceSqr = float.MaxValue;
           for (int i = 0; i < polyCount;++i)
           {
               dtPolyRef polyRef = polys[i];
               float[] closestPtPoly = new float[3];
               float[] diff = new float[3];
               bool posOverPoly = false;
               float d = 0;
               closestPointOnPoly(polyRef,center,centerStart,closestPtPoly,ref posOverPoly);
               dtVsub(diff, 0, center, centerStart, closestPtPoly, 0);
               if (posOverPoly)
               {
                   d = Math.Abs(diff[1]) - tile.header.walkableClimb;
                   d = d > 0 ? d * d : 0;
               }
               else
               {
                   d = dtVlenSqr(diff);
               }

               if (d < nearestDistanceSqr)
               {
                   dtVcopy(nearestPt, closestPtPoly);
                   nearestDistanceSqr = d;
                   nearest = polyRef;
               }
           }

           return nearest;
        }

        public void connectExtLinks(dtMeshTile tile, dtMeshTile target, int side)
        {
            if (tile == null) 
            {
                return;
            }
            for(int i=0;i<tile.header.polyCount;++i)
            {
              dtPoly poly =tile.polys[i];
              int nv = (int)poly.vertCount;
              for (int j = 0; j < nv;++j)
              {
                  if ((poly.neis[j] & DT_EXT_LINK) == 0) 
                  {
                      continue;
                  }
                  int dir =(int)(poly.neis[j]&0xff);
                  if (side != -1 && dir != side) 
                  {
                      continue;
                  }
                  int vaStart = poly.verts[j] * 3;
                  int vbStart = poly.verts[(j + 1) % nv] * 3;

                  dtPolyRef[] nei = new dtPolyRef[4];
                  float[] neia =new float[4*2];
                  
              }
            }
        }
        public dtStatus addTile(dtRawTileData rawTileData,int flags,dtTileRef lastRef,ref dtTileRef result) 
        {
            dtMeshHeader header = rawTileData.header;
            if (header.magic != DT_NAVMESH_MAGIC) 
            {
                return DT_FAILURE | DT_WRONG_MAGIC;
            }
            if (header.version != DT_NAVMESH_VERSION) 
            {
                return DT_FAILURE | DT_WRONG_VERSION;
            }
            if (getTileAt(header.x, header.y, header.layer) != null) 
            {
                return DT_FAILURE;
            }
            dtMeshTile tile = null;
            if (lastRef == 0)
            {
                if (m_nextFree != null)
                {
                    tile = m_nextFree;
                    m_nextFree = tile.next;
                    tile.next = null;
                }
            }
            else 
            {
                int tileIndex = (int)decodePolyIdTile((dtPolyRef)lastRef);
                if (tileIndex >= m_maxTiles) 
                {
                    return DT_FAILURE | DT_OUT_OF_MEMORY;
                }
                dtMeshTile target =m_tiles[tileIndex];
                dtMeshTile prev = null;
                tile = m_nextFree;
                while (tile != null && tile != target) 
                {
                    prev = tile;
                    tile = tile.next;
                }
                if (tile != target) 
                {
                    return DT_FAILURE | DT_OUT_OF_MEMORY;
                }
                if (prev == null)
                {
                    m_nextFree = tile.next;
                }
                else 
                {
                    prev.next = tile.next;
                }
                tile.salt = decodePolyIdSalt((dtPolyRef)lastRef);
            }
            if (tile == null) 
            {
                return DT_FAILURE | DT_OUT_OF_MEMORY;
            }
            int h = computeTileHash(header.x,header.y,m_tileLutMask);
            tile.next = m_posLookup[h];
            m_posLookup[h] = tile;

            tile.verts = rawTileData.verts;
            tile.polys = rawTileData.polys;
            tile.links = rawTileData.links;
            tile.detailMeshes = rawTileData.detailMeshes;
            tile.detailVerts = rawTileData.detailVerts;
            tile.detailTris = rawTileData.detailTris;
            tile.bvTree = rawTileData.bvTree;
            tile.offMeshCons = rawTileData.offMeshCons;

            tile.linksFreeList = 0;
            tile.links[header.maxLinkCount - 1].next = DT_NULL_LINK;
            for (int i = 0; i < header.maxLinkCount - 1;++i) 
            {
              tile.links[i].next =(dtTileRef)(i+1);
            }

            tile.header = header;
            tile.data = rawTileData;
            tile.flags = flags;

            connectIntLinks(tile);
            baseOffMeshLinks(tile);

            const int MAX_NEIS = 32;
            dtMeshTile[] neis = new dtMeshTile[MAX_NEIS];
            int nneis;

            nneis = getTilesAt(header.x,header.y,neis,MAX_NEIS);
            for (int j = 0; j < nneis; ++j)
            {
                if (neis[j] != tile)
                {
                    //connectExtLinks(tile, neis[j], -1);
                    //connectExtLinks(neis[j], tile, -1);
                }
                //connectExtOffMeshLinks(tile, neis[j], -1);
                //connectExtOffMeshLinks(neis[j], tile, -1);
            }
            return DT_SUCCESS;
        }
    }

}